package com.keqiang.chart.impl.axis

import android.graphics.Canvas
import android.text.TextPaint
import com.keqiang.chart.base.BaseChart
import com.keqiang.chart.base.BaseChartView
import com.keqiang.chart.base.IEmptyDataRender
import com.keqiang.chart.impl.axis.render.IDataRender
import com.keqiang.chart.impl.axis.render.IXAxisRender
import com.keqiang.chart.impl.axis.render.IYAxisRender
import com.keqiang.chart.utils.ObjectPool
import com.keqiang.chart.utils.toRadian
import com.keqiang.utils.PaintUtils
import kotlin.math.cos
import kotlin.math.sin

/**
 * 轴线图基类
 *
 * @param D 数据类型
 * @param view 轴线图绘制在指定的View上面
 * @author Created by wanggaowan on 2022/7/29 15:23
 */
abstract class BaseAxisChart<D>(view: BaseChartView<*>) : BaseChart<D>(view) {

    /**
     * x轴
     */
    abstract val xAxis: BaseXAxis

    /**
     * y轴
     */
    abstract val yAxis: BaseYAxis

    /**
     * Y轴绘制器
     */
    abstract val yAxisRender: IYAxisRender

    /**
     * X轴绘制器
     */
    abstract val xAxisRender: IXAxisRender

    /**
     * X轴与Y轴中间内容数据绘制器
     */
    abstract val dataRender: IDataRender

    override fun onDraw(canvas: Canvas, start: Float, top: Float, end: Float, bottom: Float) {
        initAxisLabel(yAxis)
        initAxisLabel(xAxis)

        // y轴绘制文本的最大宽度
        val yMaxTextWidth = getYAxisTextMaxWidth(yAxis)
        // x轴轴线左边起始位置
        val xAxisStart = start + yMaxTextWidth + yAxis.labelOffsetAxis
        // 网格宽度，x轴文本绘制的最大宽度
        val gridWidth =
            getGridWidth(xAxis, end - xAxisStart - xAxis.startPadding - xAxis.endPadding)
        // x轴文本绘制的最大高度
        val xLabelMaxHeight = getXAxisTextMaxHeight(xAxis, gridWidth, start, end, xAxisStart, end)
        // x轴轴线以下部分绘制内容高度结束位置
        val xAxisBottom = bottom - xAxis.labelOffsetAxis - xLabelMaxHeight
        //  x轴轴线y轴坐标
        val xAxisTop = xAxisBottom - xAxis.lineWidth
        // 网格高度，y轴文本绘制的最大高度，
        val gridHeight =
            getGridHeight(yAxis, xAxisTop - top - yAxis.startPadding - yAxis.endPadding)

        xAxisRender.onDraw(canvas, xAxisStart, xAxisTop, end, xAxisBottom, gridWidth, xLabelMaxHeight, xAxisTop - top)

        yAxisRender.onDraw(canvas, xAxisStart, top, (xAxisStart + yAxis.lineWidth), xAxisTop, yMaxTextWidth, gridHeight, end - xAxisStart)

        dataRender.onDraw(canvas, xAxisStart + yAxis.lineWidth + xAxis.startPadding,
            top + yAxis.endPadding, end - xAxis.endPadding, xAxisTop - yAxis.startPadding, gridWidth, gridHeight, animator)
    }

    open fun getGridWidth(xAxis: BaseXAxis, xAxisLength: Float): Float {
        var count = xAxis.labelCount - 1
        if (count < 1) {
            count = 1
        }

        return xAxisLength / count
    }

    open fun getGridHeight(yAxis: BaseYAxis, yAxisLength: Float): Float {
        var count = yAxis.labelCount - 1
        if (count < 1) {
            count = 1
        }

        return yAxisLength / count
    }

    override var emptyDataRender: IEmptyDataRender? = null

    /**
     * 初始化轴上要绘制标签列表
     */
    open fun initAxisLabel(axis: Axis) {
        if (axis.labelList != null) {
            axis.labelList?.forEach {
                it.recycle()
            }
        }

        val labelList = mutableListOf<Label>()
        for (i in 0 until axis.labelCount) {
            val label = axis.axisValueFormat.invoke(i)
            labelList.add(label)
        }
        axis.labelList = labelList
    }

    /**
     * 获取Y轴上文本绘制的最大宽度
     */
    protected open fun getYAxisTextMaxWidth(axis: BaseYAxis): Float {
        if (axis.labelWidth != null) {
            axis.labelWidth?.let {
                return if (it < axis.labelMinWidth) {
                    axis.labelMinWidth
                } else if (axis.labelMaxWidth == null || it < axis.labelMaxWidth!!) {
                    it
                } else {
                    axis.labelMaxWidth!!
                }
            }
        }

        val textPaint = ObjectPool.obtainPaint<TextPaint>(true)
        textPaint.textSize = axis.labelTextSize
        var maxTextWidth = 0f
        axis.labelList?.forEach {
            it.value?.let { label ->
                val width = textPaint.measureText(label)
                if (width > maxTextWidth) {
                    maxTextWidth = width
                }
            }
        }

        val value = if (maxTextWidth < axis.labelMinWidth) {
            axis.labelMinWidth
        } else if (axis.labelMaxWidth == null || maxTextWidth < axis.labelMaxWidth!!) {
            maxTextWidth
        } else {
            axis.labelMaxWidth!!
        }
        ObjectPool.recyclePaint(textPaint)
        return value
    }

    /**
     * 获取X轴上文本绘制的最大高度
     * @param start 水平可绘制起始点坐标
     * @param end 水平可绘制结束点坐标
     * @param xStart x轴轴线绘制起始点坐标
     * @param xEnd x轴轴线绘制结束点坐标
     */
    protected open fun getXAxisTextMaxHeight(axis: BaseXAxis, maxWidth: Float, start: Float, end: Float, xStart: Float, xEnd: Float): Float {
        if (axis.labelHeight != null) {
            axis.labelHeight?.let {
                return if (it < axis.labelMinHeight) {
                    axis.labelMinHeight
                } else if (axis.labelMaxHeight == null || it < axis.labelMaxHeight!!) {
                    it
                } else {
                    axis.labelMaxHeight!!
                }
            }
        }

        val dataList = axis.labelList
        if (dataList.isNullOrEmpty()) {
            return axis.labelMinHeight.coerceAtLeast(axis.labelMaxHeight ?: 0f)
        }

        var maxHeight = 0f
        val textPaint = ObjectPool.obtainPaint<TextPaint>(true)
        textPaint.textSize = axis.labelTextSize
        for (index in dataList.indices) {
            val label = dataList[index]
            if (label.value != null && label.weight > 0) {
                val maxWidth2 = when (index) {
                    0 -> xStart + axis.startPadding + (maxWidth * label.weight) / 2f - start
                    dataList.size - 1 -> axis.endPadding + (maxWidth * label.weight) / 2f
                    else -> (maxWidth * label.weight)
                }

                val height: Float =
                    PaintUtils.getTextHeight(textPaint, label.value!!, maxWidth2.toInt(), xAxis.labelMaxLines).toFloat()
                if (height > maxHeight) {
                    maxHeight = height
                }
            }
        }
        ObjectPool.recyclePaint(textPaint)

        if (maxHeight <= axis.labelMinHeight) {
            maxHeight = axis.labelMinHeight
        } else if (axis.labelMaxHeight != null && maxHeight > axis.labelMaxHeight!!) {
            maxHeight = axis.labelMaxHeight!!
        }
        return maxHeight
    }
}

/**
 * 3D轴线图基类
 *
 * @param D 数据类型
 * @param view 轴线图绘制在指定的View上面
 * @author Created by wanggaowan on 2022/7/29 15:23
 */
abstract class BaseAxisChart3D<D>(view: BaseChartView<*>) : BaseAxisChart<D>(view) {
    /**
     * 正面偏转的角度，顺时针旋转，取值范围 30 <= faceDegrees <= 60
     */
    var faceDegrees: Float = 30f
        set(value) {
            field = if (value < 30) {
                60f
            } else if (value > 60) {
                30f
            } else {
                (90 - value)
            }
        }

    override fun onDraw(canvas: Canvas, start: Float, top: Float, end: Float, bottom: Float) {
        initAxisLabel(yAxis)
        initAxisLabel(xAxis)

        // y轴绘制文本的最大宽度
        val yMaxTextWidth = getYAxisTextMaxWidth(yAxis)
        // x轴轴线左边起始位置
        var xAxisStart = start + yMaxTextWidth + yAxis.labelOffsetAxis
        if (xAxis is XAxis3D) {
            xAxisStart -= xAxis.lineWidth * cos(faceDegrees.toRadian)
            if (xAxisStart < 0f) {
                xAxisStart = 0f
            }
        }

        // x轴轴线右边结束位置
        var xAxisEnd = end
        if (xAxis is XAxis3D) {
            xAxisEnd -= xAxis.lineWidth * cos(faceDegrees.toRadian)
        }

        // 网格宽度，x轴文本绘制的最大宽度
        val gridWidth =
            getGridWidth(xAxis, xAxisEnd - xAxisStart - xAxis.startPadding - xAxis.endPadding)
        // x轴文本绘制的最大高度
        val xLabelMaxHeight =
            getXAxisTextMaxHeight(xAxis, gridWidth, start, end, xAxisStart, xAxisEnd)
        // x轴轴线以下部分绘制内容高度结束位置
        val xAxisBottom = bottom - xAxis.labelOffsetAxis - xLabelMaxHeight
        // x轴轴线y轴坐标
        val xAxisTop = if (xAxis is XAxis3D) {
            xAxisBottom - (xAxis as XAxis3D).lineHeight
        } else {
            xAxisBottom - xAxis.lineWidth
        }

        // y轴轴线长度
        var yRange = xAxisTop - top
        if (xAxis is XAxis3D) {
            // 减去底部矩形宽度导致的高度减少部分
            yRange -= sin(faceDegrees.toRadian) * xAxis.lineWidth
        }
        // 网格高度，y轴文本绘制的最大高度，
        val gridHeight = getGridHeight(yAxis, yRange - yAxis.endPadding - yAxis.startPadding)

        xAxisRender.onDraw(canvas, xAxisStart, xAxisTop, xAxisEnd, xAxisBottom, gridWidth, xLabelMaxHeight, yRange)

        yAxisRender.onDraw(canvas, xAxisStart, top, (xAxisStart + yAxis.lineWidth), xAxisTop, yMaxTextWidth, gridHeight, xAxisEnd - xAxisStart)

        dataRender.onDraw(canvas, xAxisStart + yAxis.lineWidth + xAxis.startPadding,
            top + yAxis.endPadding, xAxisEnd - xAxis.endPadding, xAxisTop - yAxis.startPadding, gridWidth, gridHeight, animator)
    }
}